Part 3: Personal Data Flow
| Item | Content |
|---|---|
| Document Name | Part 3: Personal Data Flow |
| Product Name | DTA Wide Sleep Management Platform |
| Date | 2026-06-18 |
| Scope | Part 3 (Backend) — limited to user personal/sensitive data |
| Related Articles | O.Purp_1/3/7, O.Data_1/2/3/5/6/7, O.Arch_2/5, O.TrdP_6/9/10, O.Ntwk_1 |
| Reference Standards | BSI TR-03161, GDPR Art. 30 (Records of processing) |
1. Purpose and Scope
This document defines the collection, processing, storage, transfer, and deletion flow of user personal and sensitive data processed by the DTA Wide backend (dta-wide-api). It serves as shared evidence for several BSI articles (see §0 mapping).
Scope limitation: This document covers personal and sensitive data only. Non-personal operational data (configuration, lookup tables, logging infrastructure) and the overall application architecture are out of scope. For system architecture see p3_01_backend_infra_architecture, for cloud outsourcing responsibility see p3_04_cloud_outsourcing_responsibility, and for cryptographic key lifecycle see p3_05_cryptographic_key_lifecycle.
Every factual statement in this document is backed by a code/schema anchor (file:line).
0. BSI Articles Covered by This Document
| Section | Covered Articles |
|---|---|
| §2 Personal data inventory & classification | O.Purp_1 (BSIA-238), O.Data_3 (314), O.Arch_2 (246) |
| §3 Data flow diagram | O.Arch_2 (246), O.Ntwk_1 (327) |
| §4 Lifecycle (collection→deletion) | O.Purp_3 (240), O.Data_2 (313), O.Data_7 (318) |
| §5 Storage map + at-rest encryption | O.Data_1 (312), O.Arch_4 (248) |
| §6 External / third-party sharing matrix | O.TrdP_6 (273), O.TrdP_9 (276), O.TrdP_10 (277), O.Purp_7 (244) |
| §7 Interface protection & notifications | O.Arch_5 (249), O.Data_5 (316), O.Data_6 (317) |
2. Personal Data Inventory and Classification
Categories of personal/sensitive data processed by the backend. Default storage is PostgreSQL (Cloud SQL, private schema).
| Category | Data item | Storage (anchor) | Classification |
|---|---|---|---|
| Identity — eID | eidHash (SHA-256, unique) | eid_links.eid_hash (private.prisma:247) | Pseudonymized |
| Identity — eID | kvnr (insurance number, persisted) | eid_links.kvnr VarChar(10) (private.prisma:248) | Sensitive |
| Identity — eID | metadata (sub·email·name [·insurer]) | eid_links.metadata Json (private.prisma:249) | Personal |
| Identity — eID | birthdate | Not stored / not used (DTO only) | — |
| Authentication | refresh/access token, app token, session | RefreshToken/AccessToken/AppToken/Session (private.prisma:162/182/220/198) | Secret |
| Authentication — 2FA | device public key (ECC P-256 SPKI) | user_device_authentications.public_key_spki (private.prisma) | Public key |
| Consent | consent/revocation history, IP, integrity HMAC | user_agreementments (private.prisma:307-329) | Personal |
| Usage history | userId, IP, User-Agent, timestamp | UsageHistory (private.prisma) | Personal |
| Health data | sleep logs / questionnaire responses (transformed) | Firestore (agent-data repositories) | Sensitive |
eID attribute storage (correction reflected): eID profile attributes are not discarded transiently; they are stored in
eid_links.metadata(Json). The stored fields differ by write path — initial signup (eu-signup-orchestrator.service.ts:270-273) storessub·email·name, while link completion (complete-eid-link.handler.ts:104-110) additionally storesinsurer.kvnris persisted to prevent ePA re-authentication (schema comment, plan 177 agenda-001) and is masked on use (Kvnr.masked()—kvnr.vo.ts:28).birthdateexists in the DTO but is neither stored nor used (cohort is determined by access-code/OAuth state).
3. Data Flow Diagram
Shows personal data collection → storage → (limited) external flow together with the trust boundary. The thick arrow (①) is inbound collection; the dotted arrow (④) is an inactive (no-transfer) path.
Flow summary: ① The app sends personal data over TLS; the API validates it (ValidationPipe) and ② stores it by category inside the trust boundary (GCP · EU, encrypted at rest — §5). The only personal/sensitive data flow that crosses the trust boundary is ③ the eID verification OAuth handshake with gematik, which carries no health data or KVNR. ④ The only external egress path for KVNR (ePA) is currently a stub, so no live transfer occurs (see §6). All inbound communication is TLS-encrypted (O.Ntwk_1, see p3_05/177-cryptography-justification).
4. Lifecycle (Collection → Processing → Storage → Transfer → Deletion)
| Stage | Processing | Control / anchor |
|---|---|---|
| Collection | Inbound data validated by global ValidationPipe (whitelist:true); undeclared fields stripped | main.ts:641-667 (O.Source_1 / BSIA-257) |
| Processing | Purpose-based minimization. Identifiers such as KVNR are masked before logging | kvnr.vo.ts:28, logger masking (177-data-purpose-mapping) |
| Storage | Per-category stores (§5). Sensitive tokens and file URLs are field-encrypted | auth.prisma:95, private.prisma:2082/2203 |
| Transfer | External transfer limited to the §6 matrix. No live external transfer of health data or KVNR | §6 |
| Deletion | On account termination eID links are deleted (eidLink.deleteMany); 2FA device keys are cascade-deleted | private.prisma (UserDeviceAuthentication onDelete: Cascade) |
For retention/deletion policy detail see
177-data-retention-policy(O.Data_2/_7).
5. Storage Map and At-Rest Encryption
| Store | Personal data stored | At-rest encryption |
|---|---|---|
| PostgreSQL (Cloud SQL) | identity (eID), authentication, consent, usage history | GCP default encryption at rest (platform) + application-level encryption for sensitive fields |
| Firestore | transformed sleep / questionnaire health data | GCP default encryption at rest (platform) |
| Redis (Memorystore) | session / cache (short-lived) | GCP default encryption at rest (platform) |
Application-level field encryption (code-confirmed):
refresh_token_cipher— encrypted storage of external IdP refresh token (auth.prisma:95)file_url_cipher— encrypted file URL (private.prisma:2203)encryption_key_id— key identifier column (private.prisma:2082, a separate model from the above)
For platform encryption and key management detail see p3_05_cryptographic_key_lifecycle, 177-cryptography-justification.
6. External / Third-Party Sharing Matrix
Complete set of paths by which the backend transfers personal data externally. (O.TrdP_6/9/10, O.Purp_7)
| Recipient | Data transferred | Purpose | Protection | Note |
|---|---|---|---|---|
| gematik eID IdP | OAuth authentication handshake | eID identity verification | TLS + OAuth/PKCE | No health data transferred |
| ePA export | (KVNR — not transferred) | ePA integration (under development) | — | Only a stub adapter is bound — no live transfer (epa-export-stub.adapter.ts:16-25, binding feature-health-data-export.module.ts:103-104) |
| GCP sub-processors | data stored per §5 | hosting · storage · messaging | DPA + region pinning (EU) | Cloud SQL/Firestore/Redis/Pub-Sub/BigQuery (p3_04) |
O.TrdP_6 (no sensitive data transfer): Health data is not transferred to third parties other than GCP sub-processors (storage purpose, under DPA). The only external egress path for KVNR is ePA export; however, as of 2026-06-18 the ePA integration is under development, and the sole implementation bound in production is a stub adapter (
EpaExportStubAdapter). OnEpaExportPort.sendthe stub performs no actual transfer — it only emits a warning log and returns{ accepted: true, referenceId: 'stub' }, and KVNR is not written to the log (epa-export-stub.adapter.ts:16-25). The domain handler'sepaPort.send(...)call (epa-export.handler.ts:158) resolves to this stub via DI binding (feature-health-data-export.module.ts:103-104); thus the call site alone may read as a live transfer, but no live transfer occurs. A live adapter will replace the stub once the TIC/Konnektor infrastructure is ready. O.TrdP_9 (notification of external services): GCP sub-processors and data-sharing details are disclosed in the cloud outsourcing document (p3_04) and the privacy policy (the user-facing disclosure screen is in P.1-Mobile/landing scope).
7. Interface Protection and Notifications
- Interface protection (O.Arch_5 / O.TrdP_10): All interfaces to external services apply TLS encryption + authentication/authorization (scope) + input validation. Inbound validation follows §4 (collection); transport encryption follows
O.Ntwk_1(p3_05/177-cryptography-justification). - Data export control (O.Data_5): Export of KVNR/health data outside its source is limited to the §6 matrix; no other export path exists.
- Notification payload (O.Data_6): Identifiers (e.g. KVNR) are masked on use (
Kvnr.masked()—kvnr.vo.ts:28). Exclusion of sensitive data from notification/push payloads is managed separately as an O.Data_6 control item.
Appendix: Supporting Code Anchors
| Fact | Anchor |
|---|---|
| eID attributes stored in metadata | complete-eid-link.handler.ts:104-110 |
| eID link model / KVNR persistence | private.prisma:244-263 |
| consent/revocation history + integrity HMAC | private.prisma:307-329 |
| KVNR masking | kvnr.vo.ts:28 |
| ePA egress = stub (no live transfer) | epa-export-stub.adapter.ts:16-25 + binding feature-health-data-export.module.ts:103-104 (call site epa-export.handler.ts:158) |
| inbound validation (ValidationPipe) | main.ts:641-667 |
| field encryption — refresh token | auth.prisma:95 (refresh_token_cipher) |
| field encryption — file URL | private.prisma:2203 (file_url_cipher), key id private.prisma:2082 (encryption_key_id, separate model) |